﻿using System;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Globalization;
using System.Collections.Generic;

using LeanEngine.Extensions;
using LeanEngine.Entity;

namespace LeanEngine.Data
{
    /// <summary>
    /// Provides methods for obtaining mapping information database.
    /// </summary>
    public static class ResultMapperExtensions
    {
        #region Extension Method LeanEngine.IDataTransferObject

        /// <summary>
        /// Maps all columns of the query result according to the attribute name 
        /// System.Data.Linq.Mapping.CollumnAttribute class property.
        /// </summary>
        /// <exception cref="System.InvalidCastException"></exception>
        /// <exception cref="BusinessException"></exception>
        /// <param name="value">The LeanEngine.IDataTransferObject instance.</param>
        /// <param name="resultSet">The set of data returned by the query.</param>
        public static void Fill(this PlainEntity value, IDataRecord resultSet)
        {
            var indexDictionary = new Dictionary<string, int>(resultSet.FieldCount);
            foreach (var metadataItem in MetaTableCache.GetItem(value.GetType()).ColumnsList)
            {
                var columnsNames = metadataItem.DBColumn.Name.Split("|");
                for (int i = 0; i < columnsNames.Length; i++)
                {
                    if (!indexDictionary.ContainsKey(columnsNames[i]))
                    {
                        indexDictionary.Add(columnsNames[i], resultSet.GetOrdinal(columnsNames[i]));
                    }

                    if (indexDictionary[columnsNames[i]].IsGreaterThanEqual(0))
                    {
                        SetNewValue(metadataItem, columnsNames[i], value, resultSet[indexDictionary[columnsNames[i]]]);
                    }
                }
            }

            var versionControler = (value as ChangeTrackerEntity);
            if (versionControler.IsNotNull()) versionControler.SaveState();
        }

        public static void SetNewValue(MetaColumn metadataItem, string columnName, object entityValue, object newValue)
        {
            if (newValue.NotIsTypeOf(typeof(DBNull)))
            {
                if (metadataItem.IsEntity)
                {
                    var relEntity = metadataItem.GetValue(entityValue) as PlainEntity;
                    if (relEntity.IsNull())
                    {
                        relEntity = (PlainEntity)metadataItem.NewEntityInstance();
                    }

                    try
                    {
                        var fkMetaTable = MetaTableCache.GetItem(relEntity.GetType());
                        var pkProperty = fkMetaTable.ColumnsList.WherePrimaryKey().First();
                        pkProperty.SetValue(relEntity, newValue);
                    }
                    catch (System.Exception ex)
                    {
                        throw new InvalidCastException("Unable to set '" + newValue.ToString() + "' into property '" + metadataItem.DBColumn.Name + "'.", ex);
                    }

                    metadataItem.SetValue(entityValue, relEntity);
                }
                else if (metadataItem.IsGuid)
                {
                    metadataItem.SetValue(entityValue, new Guid(newValue.ToString()));
                }
                else if (metadataItem.IsEnum)
                {
                    metadataItem.SetValue(entityValue, newValue);
                }
                else
                {
                    metadataItem.SetValue(entityValue, metadataItem.ChangeType(newValue));
                }
            }
        }

        #endregion
    }
}
